View Javadoc

1   // jq_NativeThread.java, created Mon Apr  9  1:52:50 2001 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
4   package joeq.Scheduler;
5   
6   import java.util.Iterator;
7   import java.util.Random;
8   import joeq.Allocator.CodeAllocator;
9   import joeq.Allocator.HeapAllocator;
10  import joeq.Allocator.RuntimeCodeAllocator;
11  import joeq.Allocator.SimpleAllocator;
12  import joeq.Class.PrimordialClassLoader;
13  import joeq.Class.jq_Class;
14  import joeq.Class.jq_DontAlign;
15  import joeq.Class.jq_InstanceMethod;
16  import joeq.Class.jq_StaticField;
17  import joeq.Class.jq_StaticMethod;
18  import joeq.Main.jq;
19  import joeq.Memory.CodeAddress;
20  import joeq.Memory.HeapAddress;
21  import joeq.Memory.StackAddress;
22  import joeq.Runtime.Debug;
23  import joeq.Runtime.StackCodeWalker;
24  import joeq.Runtime.SystemInterface;
25  import joeq.Runtime.Unsafe;
26  import jwutil.strings.Strings;
27  import jwutil.util.Assert;
28  
29  /***
30   * A jq_NativeThread corresponds to a virtual CPU in the scheduler.  There is one
31   * jq_NativeThread object for each underlying (heavyweight) kernel thread.
32   * The Java (lightweight) threads are multiplexed across the jq_NativeThreads.
33   * 
34   * @author  John Whaley <jwhaley@alum.mit.edu>
35   * @author  Miho Kurano
36   * @version $Id: jq_NativeThread.java 1941 2004-09-30 03:37:06Z joewhaley $
37   */
38  public class jq_NativeThread implements jq_DontAlign {
39  
40      /*** Trace flag.  When this is true, prints out debugging information about
41       * what is going on in the scheduler.
42       */
43      public static /*final*/ boolean TRACE = false;
44      public static /*final*/ boolean CHECK = false;
45  
46      public static final boolean STATISTICS = true;
47      
48      /*** Data structure to represent the native thread that exists at virtual
49       * machine startup.
50       */
51      public static final jq_NativeThread initial_native_thread = new jq_NativeThread(0);
52  
53      /***
54       * Initialize the initial native thread.
55       * Must be the first thing called at virtual machine startup.
56       */
57      public static void initInitialNativeThread() {
58          initial_native_thread.pid = SystemInterface.init_thread();
59          Unsafe.setThreadBlock(initial_native_thread.schedulerThread);
60          initial_native_thread.thread_handle = SystemInterface.get_current_thread_handle();
61          initial_native_thread.myHeapAllocator.init();
62          initial_native_thread.myCodeAllocator.init();
63          if (TRACE) SystemInterface.debugwriteln("Initial native thread initialized");
64      }
65  
66      /*** An array of all native threads. */
67      public static jq_NativeThread[] native_threads;
68          
69      /*** Number of Java threads that are currently active.
70       *  When this equals the number of active daemon threads, it is time to shut down.
71       */
72      private static volatile int num_of_java_threads = 0;
73      /*** Number of daemon threads that are currently active.
74       *  Daemon threads do not keep the VM running.
75       */
76      private static volatile int num_of_daemon_threads = 0;
77  
78      /*** Handle for this native thread. */
79      /*** NOTE: C code relies on this field being first. */
80      private int thread_handle;
81  
82      /*** Pointer to the Java thread that is currently executing on this native thread. */
83      /*** NOTE: C code relies on this field being second. */
84      private jq_Thread currentThread;
85  
86      /*** Process ID for this native thread. */
87      /*** NOTE: C code relies on this field being third. */
88      private int pid;
89  
90      /*** counter for preempted thread */
91      private int preempted_thread_counter;
92      
93      /*** Queue of ready Java threads. */
94      private final jq_ThreadQueue[] readyQueue;
95      /*** Queue of idle Java threads. */
96      private final jq_ThreadQueue idleQueue;
97      /*** Queue of Java threads transferred from another native thread. */
98      private final jq_SynchThreadQueue transferQueue;
99  
100     /*** Thread-local allocators. */
101     private CodeAllocator myCodeAllocator;
102     private HeapAllocator myHeapAllocator;
103 
104     /*** Original thread's stack pointer and base pointer.
105      *  These are used for longjmp'ing back to schedulerLoop when the currently
106      *  executing Java thread exits.
107      */
108     StackAddress original_esp, original_ebp;
109 
110     /*** The index of this native thread. */
111     private final int index;
112 
113     /*** The Java thread that is executing while we are in the scheduler. */
114     private final jq_Thread schedulerThread;
115 
116     public static final int MAX_NATIVE_THREADS = 16;
117 
118     private static boolean all_native_threads_initialized;
119     private static boolean all_native_threads_started;
120 
121     /*** Initialize the extra native threads.
122      * 
123      * @param nt initial native thread
124      * @param num number of native threads to initialize
125      */
126     public static void initNativeThreads(jq_NativeThread nt, int num) {
127         Assert._assert(num <= MAX_NATIVE_THREADS);
128         native_threads = new jq_NativeThread[num];
129         native_threads[0] = nt;
130         for (int i = 1; i < num; ++i) {
131             jq_NativeThread nt2 = native_threads[i] = new jq_NativeThread(i);
132             nt2.thread_handle = SystemInterface.create_thread(_nativeThreadEntry.getDefaultCompiledVersion().getEntrypoint(), HeapAddress.addressOf(nt2));
133             nt2.myHeapAllocator.init();
134             nt2.myCodeAllocator.init();
135             if (TRACE) SystemInterface.debugwriteln("Native thread " + i + " initialized");
136         }
137         all_native_threads_initialized = true;
138     }
139 
140     /*** Start up the extra native threads.
141      */
142     public static void startNativeThreads() {
143         for (int i = 1; i < native_threads.length; ++i) {
144             if (TRACE) SystemInterface.debugwriteln("Native thread " + i + " started");
145             native_threads[i].resume();
146         }
147         all_native_threads_started = true;
148     }
149 
150     /*** Returns true iff all native threads are initialized. */
151     public static boolean allNativeThreadsInitialized() {
152         return all_native_threads_initialized;
153     }
154     
155     /*** Returns the jq_Thread that is currently running on this native thread. */
156     public jq_Thread getCurrentThread() {
157         return currentThread;
158     }
159 
160     /*** Get the native thread-local code allocator. */
161     public CodeAllocator getCodeAllocator() {
162         return myCodeAllocator;
163     }
164 
165     /*** Get the native thread-local heap allocator. */
166     public HeapAllocator getHeapAllocator() {
167         return myHeapAllocator;
168     }
169 
170     /*** Get the currently-executing Java thread. */
171     public jq_Thread getCurrentJavaThread() {
172         return currentThread;
173     }
174 
175     public static final int NUM_OF_QUEUES = 10;
176     
177     /*** Create a new jq_NativeThread (only called from initNativeThreads(),
178      *  and during bootstrap initialization of initial_native_thread and
179      *  break_nthread field) */
180     private jq_NativeThread(int i) {
181         readyQueue = new jq_ThreadQueue[NUM_OF_QUEUES];
182         for (int k = 0; k < NUM_OF_QUEUES; ++k) {
183             readyQueue[k] = new jq_ThreadQueue();
184         }
185         idleQueue = new jq_ThreadQueue();
186         transferQueue = new jq_SynchThreadQueue();
187         myHeapAllocator = new SimpleAllocator();
188         myCodeAllocator = new RuntimeCodeAllocator();
189         index = i;
190         preempted_thread_counter = 0;
191         Thread t = new Thread("_scheduler_" + i);
192         currentThread = schedulerThread = ThreadUtils.getJQThread(t);
193         if (schedulerThread != null) {
194             schedulerThread.disableThreadSwitch(); // don't preempt while in the scheduler
195             schedulerThread.setNativeThread(this);
196             schedulerThread.isScheduler = true;
197         } else {
198             // schedulerThread is null if we are not native/bootstrapping.
199             Assert._assert(!jq.IsBootstrapping && !jq.RunningNative);
200         }
201     }
202 
203     /*** Create a new jq_NativeThread that is tied to a specific jq_Thread. */
204     jq_NativeThread(jq_Thread t) {
205         readyQueue = null;
206         idleQueue = null;
207         transferQueue = null;
208         myHeapAllocator = new SimpleAllocator();
209         myCodeAllocator = new RuntimeCodeAllocator();
210         index = -1;
211         currentThread = schedulerThread = t;
212         t.setNativeThread(this);
213         t.isScheduler = true;
214     }
215 
216     /*** Starts up/resumes this native thread. */
217     public void resume() {
218         SystemInterface.resume_thread(thread_handle);
219     }
220 
221     /*** Suspends this native thread. */
222     public void suspend() {
223         SystemInterface.suspend_thread(thread_handle);
224     }
225 
226     /*** Gets context of this native thread and puts it in r. */
227     public boolean getContext(jq_RegisterState r) {
228         return SystemInterface.get_thread_context(pid, r);
229     }
230 
231     /*** Sets context of this native thread to r. */
232     public boolean setContext(jq_RegisterState r) {
233         return SystemInterface.set_thread_context(pid, r);
234     }
235 
236     int chosenAsLeastBusy;
237     
238     /*** Returns the least-busy native thread. */
239     static jq_NativeThread getLeastBusyThread() {
240         int min_i = 0;
241         int min = native_threads[min_i].preempted_thread_counter +
242                   native_threads[min_i].transferQueue.length();
243         for (int i = 1; i < native_threads.length; ++i) {
244             int v = native_threads[i].preempted_thread_counter +
245                     native_threads[i].transferQueue.length();
246             if (v < min) {
247                 min = v;
248                 min_i = i;
249             }
250         }
251         if (STATISTICS)
252             native_threads[min_i].chosenAsLeastBusy++;
253         return native_threads[min_i];
254     }
255     
256     /*** Put the given Java thread on the queue of the least-busy native thread. */
257     public static void startJavaThread(jq_Thread t) {
258         // increment the global Java thread counter, and daemon thread counter if applicable.
259         _num_of_java_threads.getAddress().atomicAdd(1);
260         if (t.isDaemon())
261             _num_of_daemon_threads.getAddress().atomicAdd(1);
262 
263         // find the least-busy native thread
264         jq_NativeThread nt = getLeastBusyThread();
265         
266         Assert._assert(t.isThreadSwitchEnabled());
267         t.disableThreadSwitch(); // threads on queues have thread switch disabled
268         
269         CodeAddress ip = t.getRegisterState().getEip();
270         if (TRACE) SystemInterface.debugwriteln("Java thread " + t + " enqueued on native thread " + nt + " ip: " + ip.stringRep() + " cc: " + CodeAllocator.getCodeContaining(ip));
271         
272         // switch ourselves to be the scheduler thread, because only the scheduler
273         // thread can access the transfer queues.
274         jq_Thread my_t = Unsafe.getThreadBlock();
275         my_t.disableThreadSwitch();
276         jq_NativeThread my_nt = my_t.getNativeThread();
277         Unsafe.setThreadBlock(my_nt.schedulerThread);
278         // put the Java thread into the transfer queue of the least-busy native thread.
279         nt.transferQueue.enqueue(t);
280         // switch back to ourselves.
281         Unsafe.setThreadBlock(my_t);
282         my_t.enableThreadSwitch();
283     }
284 
285     /*** End the currently-executing Java thread and go back to the scheduler loop
286      *  to pick up another thread. 
287      */
288     public static void endCurrentJavaThread() {
289         jq_Thread t = Unsafe.getThreadBlock();
290         if (TRACE) Debug.writeln("Ending Java thread " + t);
291         Assert._assert(!t.isThreadSwitchEnabled());
292         _num_of_java_threads.getAddress().atomicSub(1);
293         if (TRACE) Debug.writeln("Number of Java threads now: " + num_of_java_threads);
294         if (t.isDaemon())
295             _num_of_daemon_threads.getAddress().atomicSub(1);
296         jq_NativeThread nt = t.getNativeThread();
297         Unsafe.setThreadBlock(nt.schedulerThread);
298         nt.currentThread = nt.schedulerThread;
299         // long jump back to entry of schedulerLoop
300         CodeAddress ip = _schedulerLoop.getDefaultCompiledVersion().getEntrypoint();
301         StackAddress fp = nt.original_ebp;
302         StackAddress sp = nt.original_esp;
303         if (TRACE) SystemInterface.debugwriteln("Long jumping back to schedulerLoop, ip:" + ip.stringRep() + " fp: " + fp.stringRep() + " sp: " + sp.stringRep());
304         HeapAddress a = (HeapAddress)sp.offset(4).peek();
305         Assert._assert(a.asObject() == nt, "arg to schedulerLoop got corrupted: " + a.stringRep() + " should be " + HeapAddress.addressOf(nt).stringRep());
306         Unsafe.longJump(ip, fp, sp, 0);
307         Assert.UNREACHABLE();
308     }
309 
310     public static boolean USE_INTERRUPTER_THREAD = false;
311 
312     public jq_InterrupterThread it;
313     
314     /*** The entry point for new native threads.
315      */
316     public void nativeThreadEntry() {
317         if (this != initial_native_thread)
318             this.pid = SystemInterface.init_thread();
319         Unsafe.setThreadBlock(this.schedulerThread);
320         Assert._assert(this.currentThread == this.schedulerThread);
321 
322         if (USE_INTERRUPTER_THREAD) {
323             // start up another native thread to periodically interrupt this one.
324             it = new jq_InterrupterThread(this);
325         } else {
326             // use setitimer
327             SystemInterface.set_interval_timer(SystemInterface.ITIMER_VIRTUAL, 10);
328         }
329 
330         // store for longJump
331         StackAddress sp = StackAddress.getStackPointer();
332         StackAddress fp = StackAddress.getBasePointer();
333         // make room for return address and "this" pointer.
334         this.original_esp = (StackAddress) sp.offset(-CodeAddress.size()-HeapAddress.size());
335         this.original_ebp = fp;
336 
337         if (TRACE) SystemInterface.debugwriteln("Started native thread: " + this + " sp: " + this.original_esp.stringRep() + " fp: " + this.original_ebp.stringRep());
338 
339         // enter the scheduler loop
340         this.schedulerLoop();
341         Assert.UNREACHABLE();
342     }
343 
344     public void schedulerLoop() {
345         HeapAddress a = (HeapAddress) this.original_esp.offset(4).peek();
346         Assert._assert(a.asObject() == this);
347 
348         // preemption cannot occur in the scheduler loop because the
349         // schedulerThread has thread switching disabled.
350         Assert._assert(Unsafe.getThreadBlock() == this.schedulerThread);
351         for (;;) {
352             // only initial native thread can shut down the VM.
353             if (this == initial_native_thread &&
354                 num_of_daemon_threads == num_of_java_threads)
355                 break;
356             Assert._assert(currentThread == schedulerThread);
357             // have to choose from which ready queue based on time slicing among 10 queues 
358             jq_Thread t = getNextReadyThread();
359             if (t == null) {
360                 // no ready threads!
361                 if (TRACE) SystemInterface.debugwriteln("Native thread " + this + " is idle!");
362                 SystemInterface.yield();
363             } else {
364                 Assert._assert(!t.isThreadSwitchEnabled());
365                 if (TRACE) SystemInterface.debugwriteln("Native thread " + this + " scheduler loop: switching to Java thread " + t);
366                 currentThread = t;
367                 SystemInterface.set_current_context(t, t.getRegisterState());
368                 Assert.UNREACHABLE();
369             }
370         }
371         dumpStatistics();
372         SystemInterface.die(0);
373         Assert.UNREACHABLE();
374     }
375 
376     public static void dumpStatistics() {
377         for (int i = 0; i < native_threads.length; ++i) {
378             native_threads[i].dumpStats();
379         }
380     }
381     
382     public void dumpStats() {
383         if (STATISTICS) {
384             Debug.write("Native thread ");
385             Debug.write(index);
386             Debug.write(": transferred out=");
387             Debug.write(transferredOut);
388             Debug.write("(");
389             Debug.write(failedTransferOut);
390             Debug.write(" failed) in=");
391             Debug.writeln(transferredIn);
392             Debug.write("               : chosen as least busy=");
393             Debug.writeln(chosenAsLeastBusy);
394             for (int i = 0; i < readyQueueLength.length; ++i) {
395                 Debug.write("               : average ready queue length ");
396                 Debug.write(i);
397                 Debug.write(": ");
398                 System.out.println((double)readyQueueLength[i] / readyQueueN);
399             }
400             Debug.write("               : preempted thread length=");
401             System.out.println((double)preemptedThreadsLength / readyQueueN);
402         }
403         if (it != null && jq_InterrupterThread.STATISTICS) {
404             Debug.write("Native thread ");
405             Debug.write(index);
406             Debug.write(": ");
407             it.dumpStatistics();
408         }
409     }
410     
411     /*** Thread switch based on a timer or poker interrupt. */
412     public void threadSwitch() {
413         jq_Thread t1 = this.currentThread;
414         t1.wasPreempted = true;
415         if (TRACE) SystemInterface.debugwriteln("Timer interrupt in native thread: " + this + " Java thread: " + t1);
416         switchThread();
417         Assert.UNREACHABLE();
418     }
419     
420     /*** Thread switch based on explicit yield. */
421     public void yieldCurrentThread() {
422         jq_Thread t1 = this.currentThread;
423         if (t1 == this.schedulerThread) {
424             // The scheduler thread is trying to yield.
425             // This occurs when there is contention on a monitor that the scheduler
426             // thread uses, e.g. the monitor for the transfer queue.
427             // The scheduler thread doesn't have anyone to yield to, so we just return,
428             // which will cause us to busy-wait.
429             t1.enableThreadSwitch();
430             Assert._assert(!t1.isThreadSwitchEnabled());
431             return;
432         }
433         t1.wasPreempted = false;
434         if (TRACE) SystemInterface.debugwriteln("Explicit yield in native thread: " + this + " Java thread: " + t1);
435         switchThread();
436         Assert.UNREACHABLE();
437     }
438     
439     private void switchThread() {
440         // thread switching for the current thread is disabled on entry.
441         jq_Thread t1 = this.currentThread;
442         Unsafe.setThreadBlock(this.schedulerThread);
443         this.currentThread = this.schedulerThread;
444         CodeAddress ip = (CodeAddress) StackAddress.getBasePointer().offset(StackAddress.size()).peek();
445         if (TRACE) SystemInterface.debugwriteln("Thread switch in native thread: " + this + " Java thread: " + t1 + " ip: " + ip.stringRep() + " cc: " + CodeAllocator.getCodeContaining(ip));
446         if (t1.isThreadSwitchEnabled()) {
447             SystemInterface.debugwriteln("Java thread " + t1 + " has thread switching enabled on threadSwitch entry!");
448             SystemInterface.die(-1);
449         }
450         Assert._assert(t1 != this.schedulerThread);
451 
452         // simulate a return in the current register state, so when the thread gets swapped back
453         // in, it will continue where it left off.
454         jq_RegisterState state = t1.getRegisterState();
455         state.setEip((CodeAddress) state.getEsp().peek());
456         state.setEsp((StackAddress) state.getEsp().offset(StackAddress.size() + CodeAddress.size()));
457 
458         // have to choose one of the 10 ready queue
459         jq_Thread t2 = getNextReadyThread();
460         transferExtraWork();
461         if (t2 == null) {
462             // only one thread!
463             t2 = t1;
464         } else {
465             ip = t2.getRegisterState().getEip();
466             if (TRACE) SystemInterface.debugwriteln("New ready Java thread: " + t2 + " ip: " + ip.stringRep() + " cc: " + CodeAllocator.getCodeContaining(ip));
467             int priority = t1.getPriority();
468             readyQueue[priority].enqueue(t1);
469             if (t1.wasPreempted && !t1.isDaemon())
470                 ++preempted_thread_counter;
471             Assert._assert(!t2.isThreadSwitchEnabled());
472         }
473         currentThread = t2;
474         SystemInterface.set_current_context(t2, t2.getRegisterState());
475         Assert.UNREACHABLE();
476     }
477 
478     public void yieldCurrentThreadTo(jq_Thread t) {
479         jq_Thread t1 = this.currentThread;
480         if (t1 == this.schedulerThread) {
481             // The scheduler thread is trying to yield.
482             // This occurs when there is contention on a monitor that the scheduler
483             // thread uses, e.g. the monitor for the transfer queue.
484             // The scheduler thread doesn't have anyone to yield to, so we just return,
485             // which will cause us to busy-wait.
486             t1.enableThreadSwitch();
487             Assert._assert(!t1.isThreadSwitchEnabled());
488             return;
489         }
490         t1.wasPreempted = false;
491         switchThread(t);
492         Assert.UNREACHABLE();
493     }
494     
495     /*** Performs a thread switch to a specific thread in our local queue. */
496     private void switchThread(jq_Thread t2) {
497         // thread switching for the current thread is disabled on entry.
498         jq_Thread t1 = this.currentThread;
499 
500         Unsafe.setThreadBlock(this.schedulerThread);
501         this.currentThread = this.schedulerThread;
502         CodeAddress ip = (CodeAddress) StackAddress.getBasePointer().offset(StackAddress.size()).peek();
503         if (TRACE) SystemInterface.debugwriteln("Thread switch in native thread: " + this + " Java thread: " + t1 + " ip: " + ip.stringRep() + " cc: " + CodeAllocator.getCodeContaining(ip));
504         if (t1.isThreadSwitchEnabled()) {
505             SystemInterface.debugwriteln("Java thread " + t1 + " has thread switching enabled on threadSwitch entry!");
506             SystemInterface.die(-1);
507         }
508         Assert._assert(t1 != this.schedulerThread);
509 
510         // simulate a return in the current register state, so when the thread gets swapped back
511         // in, it will continue where it left off.
512         jq_RegisterState state = t1.getRegisterState();
513         state.setEip((CodeAddress) state.getEsp().peek());
514         state.setEsp((StackAddress) state.getEsp().offset(StackAddress.size() + CodeAddress.size()));
515 
516         if (t1 != t2) {
517             // find given thread in our queue.
518             for (int i = 0; ; ++i) {
519                 if (i == readyQueue.length) {
520                     // doesn't exist in our ready queue.
521                     Assert.UNREACHABLE();
522                     return;
523                 }
524                 boolean exists = readyQueue[i].remove(t2); 
525                 if (exists) break;
526             }
527             if (t2.wasPreempted && !t2.isDaemon())
528                 --preempted_thread_counter;
529         }
530         transferExtraWork();
531         if (t1 != t2) {
532             ip = t2.getRegisterState().getEip();
533             if (TRACE) SystemInterface.debugwriteln("New ready Java thread: " + t2 + " ip: " + ip.stringRep() + " cc: " + CodeAllocator.getCodeContaining(ip));
534             int priority = t1.getPriority();
535             readyQueue[priority].enqueue(t1);  
536             if (t1.wasPreempted && !t1.isDaemon())
537                 ++preempted_thread_counter;
538             Assert._assert(!t2.isThreadSwitchEnabled());
539         }
540         currentThread = t2;
541         SystemInterface.set_current_context(t2, t2.getRegisterState());
542         Assert.UNREACHABLE();
543     }
544 
545     public static float TRANSFER_THRESHOLD = 1.5f;
546     
547     int transferredOut;
548     int failedTransferOut;
549     /*** Transfer extra work from our ready queue into a less-busy thread's transfer queue. */
550     private void transferExtraWork() {
551         jq_NativeThread that = getLeastBusyThread();
552         if (this == that) return;
553         // if we are much more busy than the other thread.
554         if (this.preempted_thread_counter*TRANSFER_THRESHOLD > (that.preempted_thread_counter+1)) {
555             // find a ready queue where we have more than he does
556             int i;
557             for (i = readyQueue.length-1; i >= 0; --i) {
558                 if (this.readyQueue[i].length() > that.readyQueue[i].length()) {
559                     if (STATISTICS) ++transferredOut;
560                     // transfer one thread to him.
561                     jq_Thread t2 = this.readyQueue[i].dequeue();
562                     Assert._assert(!t2.isThreadSwitchEnabled());
563                     if (t2.wasPreempted && !t2.isDaemon())
564                         --preempted_thread_counter;
565                     that.transferQueue.enqueue(t2);
566                     break;
567                 }
568             }
569             if (STATISTICS && i < 0)
570                 ++failedTransferOut;
571         }
572     }
573 
574     public boolean DETERMINISTIC = false;
575     
576     /***
577      * GCD of relatively_prime_value and the maximum value in
578      * DISTRIBUTION should be 1.
579      */
580     public static final int relatively_prime_value = 37;
581     public static final int[] DISTRIBUTION = {
582         5, 11, 18, 26, 35, 45, 56, 68, 81, 100
583     };
584     /***
585      * Keeps track of last value used, so we can compute the next value.
586      */
587     int distCounter;
588     
589     Random rng = new Random();
590     
591     private jq_ThreadQueue chooseNextQueue() {
592         if (DETERMINISTIC) {
593             distCounter += relatively_prime_value;
594             int max = DISTRIBUTION[DISTRIBUTION.length-1];
595             while (distCounter >= max) {
596                 distCounter -= max;
597             }
598         } else {
599             // use monte carlo distribution.
600             int max = DISTRIBUTION[DISTRIBUTION.length-1];
601             distCounter = rng.nextInt(max);
602         }
603         for (int i = 0; ; ++i) {
604             if (distCounter < DISTRIBUTION[i]) {
605                 if (!readyQueue[i].isEmpty()) return readyQueue[i];
606                 int c;
607                 if (DETERMINISTIC) {
608                     c = ((distCounter&1)==1) ? 1 : -1;
609                 } else {
610                     c = rng.nextBoolean() ? 1 : -1;
611                 }
612                 for (int j = i + c; j < DISTRIBUTION.length && j >= 0; j += c) {
613                     if (!readyQueue[j].isEmpty()) return readyQueue[j];
614                 }
615                 c = -c;
616                 for (int j = i + c; j < DISTRIBUTION.length && j >= 0; j += c) {
617                     if (!readyQueue[j].isEmpty()) return readyQueue[j];
618                 }
619                 return null;
620             }
621         }
622     }
623     
624     int readyQueueLength[] = new int[10];
625     int preemptedThreadsLength;
626     int readyQueueN;
627     int transferredIn;
628     /*** Get the next ready thread from the transfer queue or the ready queue.
629      *  Return null if there are no threads ready.
630      */
631     private jq_Thread getNextReadyThread() {
632         if (!transferQueue.isEmpty()) {
633             if (STATISTICS) transferredIn++;
634             jq_Thread t = transferQueue.dequeue();
635             t.setNativeThread(this);
636             Assert._assert(!t.isThreadSwitchEnabled());
637             return t;
638         }
639         if (STATISTICS) {
640             for (int i = 0; i<readyQueue.length; ++i) {
641                 readyQueueLength[i] += readyQueue[i].length();
642             }
643             preemptedThreadsLength += this.preempted_thread_counter;
644             ++readyQueueN;
645             if (CHECK) verifyCount();
646         }
647         jq_ThreadQueue q = chooseNextQueue();
648         while (q != null && !q.isEmpty()) {
649             jq_Thread t = q.dequeue();
650             if (t.wasPreempted && !t.isDaemon())
651                 --preempted_thread_counter;
652             if (!t.isAlive()) continue;
653             Assert._assert(t.getNativeThread() == this);
654             Assert._assert(!t.isThreadSwitchEnabled());
655             return t;
656         }
657         return null;
658     }
659 
660     private void verifyCount() {
661         int total = 0;
662         for (int i = 0; i < readyQueue.length; ++i) {
663             for (Iterator j = readyQueue[i].threads(); j.hasNext(); ) {
664                 jq_Thread t = (jq_Thread) j.next();
665                 if (t.wasPreempted && !t.isDaemon())
666                     ++total;
667             }
668         }
669         Assert._assert(total == preempted_thread_counter);
670     }
671     
672     public String toString() {
673         return "NT " + index + ":" + thread_handle + "(" + Strings.hex(this) + ")";
674     }
675 
676     public int getIndex() { return index; }
677 
678     public static void ctrl_break_handler() {
679         // warning: not reentrant.
680         Unsafe.setThreadBlock(break_jthread);
681         break_nthread.thread_handle = SystemInterface.get_current_thread_handle();
682         if (!has_break_occurred) {
683             break_nthread.myHeapAllocator.init();
684             break_nthread.myCodeAllocator.init();
685             has_break_occurred = true;
686         }
687         SystemInterface.debugwriteln("*** BREAK! ***");
688         for (int i = 0; i < native_threads.length; ++i) {
689             SystemInterface.suspend_thread(native_threads[i].thread_handle);
690         }
691         //dumpThreads();
692         joeq.Debugger.OnlineDebugger.debuggerEntryPoint();
693         for (int i = 0; i < native_threads.length; ++i) {
694             SystemInterface.resume_thread(native_threads[i].thread_handle);
695         }
696     }
697 
698     public static void dumpAllThreads() {
699         jq_RegisterState rs = jq_RegisterState.create();
700         rs.setContextFlags(jq_RegisterState.CONTEXT_CONTROL);
701         for (int i = 0; i < native_threads.length; ++i) {
702             SystemInterface.get_thread_context(native_threads[i].pid, rs);
703             native_threads[i].dump(rs);
704         }
705     }
706 
707     private static boolean has_break_occurred = false;
708     private static jq_NativeThread break_nthread;
709     private static jq_Thread break_jthread;
710 
711     public static void initBreakThread() {
712         break_nthread = new jq_NativeThread(-1);
713         Thread t = new Thread("_break_");
714         break_jthread = ThreadUtils.getJQThread(t);
715         break_jthread.disableThreadSwitch();
716         break_jthread.setNativeThread(break_nthread);
717         if (TRACE) SystemInterface.debugwriteln("Break thread initialized");
718     }
719 
720     // Suspend all threads except for the current one.
721     // The current thread must have thread switch disabled.
722     public static void suspendAllThreads() {
723         if (!all_native_threads_started) {
724             if (TRACE) Debug.writeln("Native threads haven't started yet.");
725             return;
726         }
727         if (TRACE) Debug.writeln("Suspending all native threads: ", native_threads.length);
728         jq_Thread t = Unsafe.getThreadBlock();
729         Assert._assert(!t.isThreadSwitchEnabled());
730         for (int i = 0; i < native_threads.length; ++i) {
731             jq_NativeThread nt = native_threads[i];
732             if (nt == t.getNativeThread()) continue;
733             for (;;) {
734                 if (TRACE) Debug.writeln("Attempting to suspend native thread ", i);
735                 nt.suspend();
736                 jq_Thread t2 = nt.getCurrentJavaThread();
737                 if (t2.isThreadSwitchEnabled()) break;
738                 if (TRACE) Debug.writeln("Failed, trying again.");
739                 nt.resume();
740                 SystemInterface.msleep(0);
741             }
742             if (TRACE) Debug.writeln("Success!");
743         }
744         if (TRACE) Debug.writeln("Finished suspending native threads");
745     }
746     
747     public static void resumeAllThreads() {
748         if (!all_native_threads_started) {
749             if (TRACE) Debug.writeln("Native threads haven't started yet.");
750             return;
751         }
752         if (TRACE) Debug.writeln("Resuming all native threads");
753         jq_Thread t = Unsafe.getThreadBlock();
754         Assert._assert(!t.isThreadSwitchEnabled());
755         for (int i = 0; i < native_threads.length; ++i) {
756             jq_NativeThread nt = native_threads[i];
757             if (nt == t.getNativeThread()) continue;
758             if (TRACE) Debug.writeln("Resuming native thread ", i);
759             nt.resume();
760         }
761         if (TRACE) Debug.writeln("All native threads resumed");
762     }
763     
764     public void dump(jq_RegisterState regs) {
765         SystemInterface.debugwriteln(this + ": current Java thread = " + currentThread);
766         StackCodeWalker.stackDump(regs.getEip(), regs.getEbp());
767         for (int i = 0; i < readyQueue.length; ++i) {
768             SystemInterface.debugwriteln(this + ": ready queue "+i+" = " + readyQueue[i]); 
769         }
770         SystemInterface.debugwriteln(this + ": idle queue = " + idleQueue);
771         SystemInterface.debugwriteln(this + ": transfer queue = " + transferQueue);
772     }
773 
774     public jq_ThreadQueue getReadyQueue(int i) {
775         return this.readyQueue[i];
776     }
777     public jq_ThreadQueue getIdleQueue() {
778         return this.idleQueue;
779     }
780     public jq_ThreadQueue getTransferQueue() {
781         return this.transferQueue;
782     }
783 
784     public static final jq_Class _class;
785     public static final jq_InstanceMethod _nativeThreadEntry;
786     public static final jq_InstanceMethod _schedulerLoop;
787     public static final jq_InstanceMethod _threadSwitch;
788     public static final jq_StaticMethod _ctrl_break_handler;
789     public static final jq_StaticField _num_of_java_threads;
790     public static final jq_StaticField _num_of_daemon_threads;
791 
792     static {
793         _class = (jq_Class) PrimordialClassLoader.loader.getOrCreateBSType("Ljoeq/Scheduler/jq_NativeThread;");
794         _nativeThreadEntry = _class.getOrCreateInstanceMethod("nativeThreadEntry", "()V");
795         _schedulerLoop = _class.getOrCreateInstanceMethod("schedulerLoop", "()V");
796         _threadSwitch = _class.getOrCreateInstanceMethod("threadSwitch", "()V");
797         _ctrl_break_handler = _class.getOrCreateStaticMethod("ctrl_break_handler", "()V");
798         _num_of_java_threads = _class.getOrCreateStaticField("num_of_java_threads", "I");
799         _num_of_daemon_threads = _class.getOrCreateStaticField("num_of_daemon_threads", "I");
800     }
801 }